package bsSync

import (
	"bytes"
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"gosync/message"
	"io"
	"log/slog"
	"math/big"
	"net/http"
	"strings"
	"sync"
)

type SyncHostConfig struct {
	GetDataHost           string
	ReceiveDataHost       string
	SelfServiceTimeStamp  int64 `json:"self_service_timestamp"`
	ThirdServiceTimeStamp int64 `json:"third_service_timestamp"`
	Ctx                   context.Context
}

type ResponseTableIdList struct {
	List             []string `json:"list"`
	ServiceTimeStamp int64
	RequestUniqueId  string
	SyncGroup        []string `json:"sync_group"`
	SyncUpdateGroup  []string `json:"sync_update_group"`
}

type CommonResponse struct {
	Status bool        `json:"status"`
	Msg    string      `json:"msg"`
	Data   interface{} `json:"data"`
	Code   int         `json:"code"`
}

type TotalCountResponse struct {
	TotalCount int64 `json:"total_count"`
	PageSize   int64 `json:"page_size"`
	TotalPage  int64 `json:"total_page"`
}

type FileIdResponse struct {
	IdList []string
}

type FileBase64Response struct {
	Base64       string `json:"base64"`
	RelativePath string `json:"relative_path"`
}

// 获取需要同步的表的配置
func (obj *SyncHostConfig) GetSyncTableId() ResponseTableIdList {
	responseTableIdList := ResponseTableIdList{}
	getSyncConfigUrl := obj.GetDataHost + "/manager/roo/sync/get_sync_config"
	formData := map[string]interface{}{"_method": "get", "sync_add": 1}
	data := RequestUrl(getSyncConfigUrl, formData, "POST", obj.Ctx)
	bData, _ := json.Marshal(data)
	ok := json.Unmarshal(bData, &responseTableIdList)
	if ok != nil {
		panic("获取同步表ID报错:" + ok.Error())
	}
	return responseTableIdList
}

// 请求服务器时间
func (obj *SyncHostConfig) GetServiceTimeStamp() [2]bool {
	var urlList [2]string
	var srvState [2]bool
	var wg sync.WaitGroup
	urlList[0] = obj.GetDataHost + "/manager/syncData/get_sys_config"
	urlList[1] = obj.ReceiveDataHost + "/manager/syncData/get_sys_config"
	message.LogMsg.InfoContext(obj.Ctx, "开始获取服务器时间")
	for key, url := range urlList {
		formData := map[string]string{"_method": "get"}
		wg.Add(1)
		go func(key int, url string) {
			defer CatchException()
			defer wg.Done()
			ctx := message.AppendCtx(obj.Ctx, slog.String("CustomizeCoroutineId", GenerateRandomNumberString(8)))
			data := RequestUrl(url, formData, "POST", ctx)
			dataMap, ok := data.(map[string]interface{})
			if !ok {
				panic(url + "解析data结果失败")
			}
			serviceTimestamp, ok := dataMap["service_timestamp"].(float64)
			fmt.Println(serviceTimestamp, ok)
			if ok {
				srvState[key] = true
				if key == 0 {
					obj.ThirdServiceTimeStamp = int64(serviceTimestamp)
				} else {
					obj.SelfServiceTimeStamp = int64(serviceTimestamp)
				}
			}

		}(key, url)
	}
	wg.Wait()
	message.LogMsg.InfoContext(obj.Ctx, "结束获取服务器时间")
	return srvState

}

// 存储待同步数据 addUpdateLabel=add 新增 update 更新
func (obj *SyncHostConfig) StoreAddUpdateData(tableConfigIdList []string, addUpdateLabel string) {
	for i := len(tableConfigIdList) - 1; i >= 0; i-- {
		tableConfigIds := tableConfigIdList[i]
		tableConfigIdArr := strings.Split(tableConfigIds, ",")
		fmt.Println(tableConfigIdArr)
		for _, tableConfigId := range tableConfigIdArr {
			wg := sync.WaitGroup{}
			wg.Add(1)
			go func(tableConfigId string) {
				defer CatchException()
				defer wg.Done()
				ctx := message.AppendCtx(obj.Ctx, slog.String("CustomizeCoroutineId", GenerateRandomNumberString(8)))
				message.LogMsg.InfoContext(ctx, "观察同步顺序", tableConfigId, tableConfigIdArr)
				randStr := GenerateRandomNumberString(10)
				storeUrl := obj.GetDataHost + "/manager/roo/sync/store_sync_data?requestId=" + randStr
				formData := map[string]string{"table_config_id": tableConfigId, "sync_data_type": addUpdateLabel, "_method": "get", "requestId": randStr}
				responseData := RequestUrl(storeUrl, formData, "POST", ctx)
				fmt.Println(responseData)
			}(tableConfigId)
			wg.Wait()
		}
	}

}

// 同步数据
func (obj *SyncHostConfig) SyncDataToDataBase(tableConfigIdList []string, addUpdateLabel string) {
	for _, tableConfigIds := range tableConfigIdList {
		wg := sync.WaitGroup{}
		tableConfigIdArr := strings.Split(tableConfigIds, ",")
		for _, tableConfigId := range tableConfigIdArr {
			wg.Add(1)
			go func(tableConfigId string) {
				defer CatchException()
				defer wg.Done()
				defer func() {
					if r := recover(); r != nil {
						fmt.Println("Recovered from panic:", r)
					}
				}()
				formData := map[string]interface{}{
					"_method":                 "get",
					"sync_data_type":          addUpdateLabel,
					"id":                      tableConfigId,
					"self_service_timestamp":  obj.SelfServiceTimeStamp,
					"third_service_timestamp": obj.ThirdServiceTimeStamp,
				}
				//分页请求
				obj.QueryDataToSyncThenCallResult(formData)
				// formByte, _ := json.Marshal(formData)
				// body := RequestJson(getDataUrl, formByte, "POST")
				// result := RequestJson(receviewSyncDataUrl, body, "POST")
				// //异步回调不需要等结果
				// go func(result []byte) {
				// 	RequestJson(callUpdateResultUrl, result, "POST")
				// }(result)

			}(tableConfigId)
		}
		wg.Wait()
	}
}

// 查询数据同步并回调同步结果
func (obj *SyncHostConfig) QueryDataToSyncThenCallResult(formData map[string]interface{}) {
	totalCountResponse := TotalCountResponse{}
	countDataUrl := obj.GetDataHost + "/manager/roo/sync/get_sync_data_count"
	getDataUrl := obj.GetDataHost + "/manager/roo/sync/get_sync_data"
	receviewSyncDataUrl := obj.ReceiveDataHost + "/manager/syncData/receive_sync_data"
	callUpdateResultUrl := obj.GetDataHost + "/manager/roo/sync/update_sync_result"
	ctx := message.AppendCtx(obj.Ctx, slog.String("CustomizeCoroutineId", GenerateRandomNumberString(8)))
	responseData := RequestUrl(countDataUrl, formData, "POST", ctx)
	fmt.Println(responseData)
	data, _ := json.Marshal(responseData)
	ok := json.Unmarshal(data, &totalCountResponse)
	if ok != nil {
		panic("接口地址报错:" + countDataUrl)
	}

	fmt.Println("totalCountResponse", totalCountResponse)

	for i := 1; i <= int(totalCountResponse.TotalPage); i++ {
		formData["page"] = i
		formData["page_size"] = totalCountResponse.PageSize
		formByte, _ := json.Marshal(formData)
		body := RequestJson(getDataUrl, formByte, "POST", ctx)
		fmt.Println("TableConfigId:", formData["id"], "第", i, "次请求", getDataUrl)
		result := RequestJson(receviewSyncDataUrl, body, "POST", ctx)
		//异步回调不需要等结果
		go func(result []byte) {
			defer CatchException()
			RequestJson(callUpdateResultUrl, result, "POST", ctx)
		}(result)
	}

}

// 获取有更新同步数据的表
func (obj *SyncHostConfig) GetHadUpdateDataTable() ResponseTableIdList {
	responseTableIdList := ResponseTableIdList{}
	getDataUrl := obj.GetDataHost + "/manager/roo/sync/get_sync_config"
	formData := map[string]interface{}{"_method": "get", "has_update": 1}
	data := RequestUrl(getDataUrl, formData, "POST", obj.Ctx)
	bData, _ := json.Marshal(data)
	ok := json.Unmarshal(bData, &responseTableIdList)
	if ok != nil {
		panic("报错->获取有更新同步数据的表:" + ok.Error())
	}
	fmt.Println("本次需要同步的表:", responseTableIdList)

	return responseTableIdList
}

func (obj *SyncHostConfig) SyncDataFlow() {

	//获取数据库时间
	srvState := obj.GetServiceTimeStamp()
	fmt.Println("服务状态:", srvState)
	if !srvState[0] || !srvState[1] {
		fmt.Println("有服务异常", srvState)
		return
	}
	//获取需要同步的表
	responseTableIdList := obj.GetSyncTableId()
	//根据新需要配置的表去保存本次新增的数据
	obj.StoreAddUpdateData(responseTableIdList.SyncGroup, "add")
	//根据新需要配置的表去保存本次更新的数据
	obj.StoreAddUpdateData(responseTableIdList.SyncUpdateGroup, "update")
	//获取有更数据的表
	responseTableIdList = obj.GetHadUpdateDataTable()
	//获取本次新增的数据同步到对方数据库
	obj.SyncDataToDataBase(responseTableIdList.SyncGroup, "add")
	//获取本次新增的数据同步到对方数据库
	obj.SyncDataToDataBase(responseTableIdList.SyncUpdateGroup, "update")
	//统计更新数据
	obj.UpdateCount()
}

func (obj *SyncHostConfig) UpdateCount() {
	updateCountUrl := obj.GetDataHost + "/manager/roo/sync/update_sync_count"
	RequestUrl(updateCountUrl, nil, "POST", obj.Ctx)
}

func RequestUrl(url string, raw interface{}, method string, ctx context.Context) interface{} {

	message.LogMsg.InfoContext(ctx, "发送请求URL:"+url, "params", raw)

	commonResponse := CommonResponse{}
	jsonData, _ := json.Marshal(raw)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		panic("构建请求接口:" + url + "报错")
	}
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	defer resp.Body.Close()
	if err != nil {
		message.LogMsg.InfoContext(ctx, "请求接口URL:"+url+"报错"+err.Error())
		panic("请求接口:" + url + "报错")
	}

	body, err := io.ReadAll(resp.Body)
	message.LogMsg.InfoContext(ctx, "请求响应结果:"+url, "response statusCode:", resp.StatusCode, "响应长度:", len(body))
	if err != nil {
		message.LogMsg.InfoContext(ctx, "读取数据报错:"+url+"报错"+err.Error())
		panic("读取数据报错" + url)
	}

	ok := json.Unmarshal(body, &commonResponse)
	if ok != nil {
		panic("请求接口" + url + "解析数据报告:" + ok.Error())
	}
	message.LogMsg.InfoContext(ctx, "请求响应结果:"+url, "response", commonResponse)
	return commonResponse.Data
}

func RequestJson(url string, raw []byte, method string, ctx context.Context) []byte {
	message.LogMsg.InfoContext(ctx, "发送请求URL:"+url, "params", string(raw))
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(raw))
	if err != nil {
		panic("构建请求接口:" + url + "报错" + err.Error())
	}
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic("请求接口:" + url + "报错" + err.Error())
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		panic("读取数据报错" + url + err.Error())
	}
	message.LogMsg.InfoContext(ctx, "请求响应结果:"+url, "respose", string(body))
	return body

}

func RequestUrlCommonResponse(url string, raw interface{}, method string) CommonResponse {

	commonResponse := CommonResponse{}
	jsonData, _ := json.Marshal(raw)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		panic("构建请求接口:" + url + "报错")
	}
	req.Header.Set("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		panic("请求接口:" + url + "报错")
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		panic("读取数据报错" + url)
	}

	ok := json.Unmarshal(body, &commonResponse)

	if ok != nil {
		panic("请求接口" + url + "解析数据报告:" + ok.Error())
	}
	return commonResponse
}

func GenerateRandomNumberString(length int) string {
	const charset = "ABCDEFGHIJKLMNOPARSTUVWXYZ0123456789"
	result := make([]byte, length)
	charsetLength := big.NewInt(int64(len(charset)))

	for i := range result {
		randomInt, err := rand.Int(rand.Reader, charsetLength)
		if err != nil {
			return "[ooooooooiiiii]"
		}
		result[i] = charset[randomInt.Int64()]
	}

	return string(result)
}

func (obj *SyncHostConfig) SyncFile() {
	defer CatchException()
	needSyncFileIdUrl := obj.GetDataHost + "/manager/roo/sync/get_sync_file_id"
	getSyncFileContentUrl := obj.GetDataHost + "/manager/roo/sync/get_sync_file_content"
	receiveSyncFileUrl := obj.ReceiveDataHost + "/manager/roo/sync/receive_file"

	//查询同步文件ID
	formData := map[string]interface{}{"_method": "get"}
	dataResponse := RequestUrl(needSyncFileIdUrl, formData, "POST", obj.Ctx)
	fileIdList := make([]string, 100)
	data, ok := json.Marshal(dataResponse)
	if ok != nil {
		panic(ok.Error() + needSyncFileIdUrl)
	}
	err := json.Unmarshal(data, &fileIdList)
	if err != nil {
		panic(err.Error() + needSyncFileIdUrl)
	}
	if len(fileIdList) > 0 {

		i := 0
		page := 5
		idCount := len(fileIdList)
		for {
			wg := sync.WaitGroup{}
			if i > idCount {
				break
			}
			maxSize := i + page
			if maxSize >= idCount {
				maxSize = idCount
			}
			idList := fileIdList[i:maxSize]
			fmt.Println(idList, "Form", i, "To", maxSize)
			i = i + page
			if len(idList) == 0 {
				break
			}

			for _, id := range idList {
				wg.Add(1)
				go func(fileId string) {
					defer func() {
						if r := recover(); r != nil {
							fmt.Println("Recovered from panic:", r)
							obj.callSyncFileResult(fileId, 2)
						}
					}()
					defer wg.Done()
					ctx := message.AppendCtx(obj.Ctx, slog.String("CustomizeCoroutineId", GenerateRandomNumberString(8)))
					//获取文件base64内容
					formData := map[string]interface{}{"id": fileId, "_method": "get"}
					fileBody := RequestUrl(getSyncFileContentUrl, formData, "POST", ctx)
					data, err := json.Marshal(fileBody)
					if err != nil {
						panic(getSyncFileContentUrl + err.Error())
					}
					fileBase64 := FileBase64Response{}
					err = json.Unmarshal(data, &fileBase64)
					if err != nil {
						panic("解析失败" + getSyncFileContentUrl + err.Error())
					}
					//同步文件base64到另一台服务
					formData = map[string]interface{}{"base64": fileBase64.Base64, "relative_path": fileBase64.RelativePath, "_method": "post"}
					commonResponse := RequestUrlCommonResponse(receiveSyncFileUrl, formData, "POST")
					//回调同步结果
					if commonResponse.Status {
						obj.callSyncFileResult(fileId, 1)
					} else {
						obj.callSyncFileResult(fileId, 2)
					}
				}(id)
			}
			wg.Wait()
		}
	}

}

func CatchException() {
	if r := recover(); r != nil {
		fmt.Println("Recovered from panic:------1", r)
	}
}

func (obj *SyncHostConfig) callSyncFileResult(fileId string, syncFlag int64) {
	updateSyncFileIdUrl := obj.GetDataHost + "/manager/roo/sync/update_sync_file_flag"
	formData := map[string]interface{}{"file_id": fileId, "sync_flag": syncFlag, "_method": "POST"}
	RequestUrlCommonResponse(updateSyncFileIdUrl, formData, "POST")
}
